/**
 * Copyright 2009 Webmedia Group Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 **/

package ee.webmedia.xtee.client.service.callback;

import java.util.Collection;
import java.util.Random;

import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;

import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.soap.saaj.SaajSoapMessage;

import ee.webmedia.xtee.client.service.configuration.XTeeServiceConfiguration;
import ee.webmedia.xtee.model.XTeeAttachment;

/**
 * <code>WebServiceMessageCallback</code> implementation that sets envelope namespaces for SOAP envelope, adds XTee
 * query header elements ('asutus', 'andmekogu', 'isikukood', etc -- as specified by {@link XTeeServiceConfiguration})
 * and adds the attachments to message.
 * 
 * @author Rando Mihkelsaar
 */
public class XTeeMessageCallback implements WebServiceMessageCallback {

  protected Random random = new Random(System.currentTimeMillis());

  private final XTeeServiceConfiguration serviceConfiguration;
  private final Collection<XTeeAttachment> attachments;

  public XTeeMessageCallback(XTeeServiceConfiguration serviceConfiguration, Collection<XTeeAttachment> attachments) {
    this.serviceConfiguration = serviceConfiguration;
    this.attachments = attachments;
  }

  public void doWithMessage(WebServiceMessage message) {
    SaajSoapMessage saajMessage = (SaajSoapMessage) message;
    try {
      // Add attachments
      if (attachments != null) {
        for (XTeeAttachment attachment : attachments) {
          saajMessage.addAttachment("<" + attachment.getCid() + ">", attachment, attachment.getContentType());
        }
      }
      SOAPMessage soapmess = saajMessage.getSaajMessage();
      SOAPEnvelope env = soapmess.getSOAPPart().getEnvelope();

      addNamespaces(env);
      addXTeeHeaderElements(env);
    } catch (SOAPException e) {
      throw new RuntimeException(e);
    }
  }

  private void addXTeeHeaderElements(SOAPEnvelope env) throws SOAPException {
    SOAPHeader header = env.getHeader();
    SOAPElement pasutus = header.addChildElement("asutus", "ns4");
    SOAPElement pandmekogu = header.addChildElement("andmekogu", "ns4");
    SOAPElement ikood = header.addChildElement("isikukood", "ns4");
    SOAPElement id = header.addChildElement("id", "ns4");
    SOAPElement pnimi = header.addChildElement("nimi", "ns4");
    String toimik = serviceConfiguration.getFile();
    if (toimik != null) {
      SOAPElement toimikEl = header.addChildElement("toimik", "ns4");
      toimikEl.addTextNode(toimik);
    }

    pasutus.addAttribute(env.createName("xsi:type"), "xsd:string");
    pandmekogu.addAttribute(env.createName("xsi:type"), "xsd:string");
    ikood.addAttribute(env.createName("xsi:type"), "xsd:string");
    id.addAttribute(env.createName("xsi:type"), "xsd:string");
    pnimi.addAttribute(env.createName("xsi:type"), "xsd:string");
    pasutus.addTextNode(serviceConfiguration.getInstitution());
    pandmekogu.addTextNode(serviceConfiguration.getDatabase());
    ikood.addTextNode(serviceConfiguration.getIdCode());
    id.addTextNode(generateUniqueMessageId());
    StringBuilder sb = new StringBuilder(serviceConfiguration.getDatabase());
    sb.append(".");
    sb.append(serviceConfiguration.getMethod());
    sb.append(".");
    sb.append(serviceConfiguration.getVersion() == null ? "v1" : serviceConfiguration.getVersion());
    pnimi.addTextNode(sb.toString());

//  deprecated: http://ftp.ria.ee/pub/x-tee/doc/nouded_infosysteemidele.pdf
    if(serviceConfiguration.useDeprecatedApi()) {
      SOAPElement pametnik = header.addChildElement("ametnik", "ns4");
      pametnik.addAttribute(env.createName("xsi:type"), "xsd:string");
      pametnik.addTextNode(serviceConfiguration.getIdCode());
    }
  }

  /**
   * Unique identifier for service invocation, consisting of numbers and letters of the Latin alphabet. The identifier
   * is generated by service invoker, who must guarantee that the identifier is globally unique.
   */
  private String generateUniqueMessageId() {
    return Long.toHexString(System.currentTimeMillis()) + serviceConfiguration.getInstitution() + random.nextInt();
  }

  /**
   * sets envelope namespaces to SOAP envelope
   */
  private void addNamespaces(SOAPEnvelope env) throws SOAPException {
    env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
    env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    env.addNamespaceDeclaration("SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/");
    env.addNamespaceDeclaration("ns4", "http://x-tee.riik.ee/xsd/xtee.xsd");
    env.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
  }

  public XTeeServiceConfiguration getServiceConfiguration() {
    return serviceConfiguration;
  }

}
